package org.jeecg.modules.wms.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.wms.entity.*;
import org.jeecg.modules.wms.mapper.WmsOutboundMapper;
import org.jeecg.modules.wms.service.*;
import org.jeecg.modules.wms.vo.WmsOutboundVO;
import org.jeecg.modules.wms.vo.WmsOutboundDetailVO;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.event.RequisitionOutboundEvent;
import org.jeecg.modules.oms.service.IProductSalesOrderService;
import org.jeecg.modules.wms.vo.OutboundAllocationResult;
import org.springframework.beans.BeanUtils;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import org.apache.commons.lang3.StringUtils;

/**
 * @Description: 出库单服务实现类
 * @Author: jeecg-boot
 * @Date: 2023-07-01
 * @Version: V1.0
 */
@Service
@Slf4j
public class WmsOutboundServiceImpl extends ServiceImpl<WmsOutboundMapper, WmsOutbound> implements IWmsOutboundService {
    
    @Autowired
    private IWmsOutboundDetailService wmsOutboundDetailService;
    
    @Autowired
    private IWmsInventoryService wmsInventoryService;
    
    @Autowired
    private IWmsInventoryHistoryService wmsInventoryHistoryService;
    
    @Autowired
    private ISysBaseAPI sysBaseAPI;
    
    @Autowired
    private IProductSalesOrderService productSalesOrderService;
    
    @Autowired
    private IWmsOutboundAllocationPlanService wmsOutboundAllocationPlanService;

    @Autowired
    private IWmsWarehouseLocationService locationService;
    
    @Autowired
    private IWmsInboundOrderService wmsInboundOrderService;

    @Autowired
    private IWmsWarehouseService wmsWarehouseService;
    
    @Autowired
    private org.jeecg.modules.wms.config.SurplusInboundConfig surplusInboundConfig;
    
    @Value("${jeecg.path.upload}")
    private String uploadPath;

    @Override
    public IPage<WmsOutboundVO> queryOutboundList(Page<WmsOutboundVO> page, WmsOutbound wmsOutbound) {
        return baseMapper.queryOutboundList(page, wmsOutbound);
    }
    
    @Override
    public WmsOutboundVO queryById(String id) {
        WmsOutboundVO wmsOutboundVO = baseMapper.queryOutboundById(id);
        if (wmsOutboundVO != null) {
            List<WmsOutboundDetail> detailList = wmsOutboundDetailService.selectByOutboundId(id);
            // 转换 WmsOutboundDetail 到 WmsOutboundDetailVO
            List<WmsOutboundDetailVO> detailVOList = detailList.stream().map(detail -> {
                WmsOutboundDetailVO detailVO = new WmsOutboundDetailVO();
                BeanUtils.copyProperties(detail, detailVO);
                return detailVO;
            }).collect(Collectors.toList());

            wmsOutboundVO.setOutboundDetailList(detailVOList);
        }
        return wmsOutboundVO;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean add(WmsOutboundVO wmsOutboundVO) {
        // 设置出库单号
        String dateStr = String.format("%tY%<tm%<td", new Date());
        String timeStr = String.format("%tH%<tM%<tS", new Date());
        String outboundNo = "QT-CK-" + dateStr + "-" + timeStr;
        wmsOutboundVO.setOutboundNo(outboundNo);
        // 设置初始状态为待审核
        wmsOutboundVO.setStatus("1");
        // 保存主表信息
        boolean result = save(wmsOutboundVO);
        if (result && wmsOutboundVO.getOutboundDetailList() != null && !wmsOutboundVO.getOutboundDetailList().isEmpty()) {
            // 根据出库方案生成明细信息
            List<WmsOutboundDetail> detailList = generateOutboundDetailsFromPlan(wmsOutboundVO.getOutboundDetailList());
            // 保存明细信息
            wmsOutboundDetailService.saveBatch(wmsOutboundVO.getId(), detailList);
        }
        return result;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean edit(WmsOutboundVO wmsOutboundVO) {
        // 只有草稿状态才能编辑
        WmsOutbound outbound = getById(wmsOutboundVO.getId());
        if (outbound == null || !"0".equals(outbound.getStatus())) {
            return false;
        }
        
        // 更新主表信息
        boolean result = updateById(wmsOutboundVO);
        if (result) {
            // 删除原有明细
            wmsOutboundDetailService.deleteByOutboundId(wmsOutboundVO.getId());
            // 保存新明细
            if (wmsOutboundVO.getOutboundDetailList() != null && !wmsOutboundVO.getOutboundDetailList().isEmpty()) {
                // 根据出库方案生成明细信息
                List<WmsOutboundDetail> detailList = generateOutboundDetailsFromPlan(wmsOutboundVO.getOutboundDetailList());
                wmsOutboundDetailService.saveBatch(wmsOutboundVO.getId(), detailList);
            }
        }
        return result;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delete(String id) {
        // 只有草稿状态才能删除
        WmsOutbound outbound = getById(id);
        if (outbound == null || !"0".equals(outbound.getStatus())) {
            return false;
        }
        
        // 删除明细
        wmsOutboundDetailService.deleteByOutboundId(id);
        // 删除主表
        return removeById(id);
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean cancel(String id) {
        WmsOutbound outbound = getById(id);
        // 只有草稿、待审核、已审核状态才能取消
        if (outbound == null || !("0".equals(outbound.getStatus()) || "1".equals(outbound.getStatus()) || "2".equals(outbound.getStatus()))) {
            return false;
        }
        
        // 更新状态为已取消
        WmsOutbound updateOutbound = new WmsOutbound();
        updateOutbound.setId(id);
        updateOutbound.setStatus("4");
        return updateById(updateOutbound);
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean audit(String id, boolean isPass, String remark) {
        WmsOutbound outbound = getById(id);
        // 只有待审核状态才能审核
        if (outbound == null || !"1".equals(outbound.getStatus())) {
            return false;
        }
        
        try {
            // 更新出库单状态
            WmsOutbound updateOutbound = new WmsOutbound();
            updateOutbound.setId(id);
            if (isPass) {
                // 审核通过，状态改为已审核
                updateOutbound.setStatus("2");
            } else {
                // 审核不通过，状态改为已取消
                updateOutbound.setStatus("4");
            }
            //在原有的备注上另起一行添加审核结果
            if(oConvertUtils.isNotEmpty(remark)){
                remark = "审核结果：" + remark;
                updateOutbound.setRemark(oConvertUtils.isNotEmpty(outbound.getRemark()) ? outbound.getRemark() + ";" +remark :  remark);
            }
            boolean updateResult = updateById(updateOutbound);
            
            if (updateResult && isPass) {
                // 审核通过时，自动分配出库方案
                try {
                    log.info("开始为出库单 {} 自动分配出库方案", id);
                    OutboundAllocationResult allocationResult = wmsOutboundAllocationPlanService.autoAllocateOutbound(id);
                    
                    if (allocationResult.isSuccess()) {
                        log.info("出库单 {} 分配方案生成成功，完全满足需求：{}", id, allocationResult.isFullySatisfied());
                        
                        // 更新出库明细的分配状态
                        updateOutboundDetailAllocationStatus(id, allocationResult);
                        
                        // 如果分配不完全满足需求，记录警告日志
                        if (!allocationResult.isFullySatisfied()) {
                            log.warn("出库单 {} 存在库存不足的物料：{}", id, 
                                allocationResult.getUnallocatedMaterials().size());
                            for (OutboundAllocationResult.UnallocatedMaterial material : allocationResult.getUnallocatedMaterials()) {
                                log.warn("物料 {} 库存不足，需求：{}，可用：{}，缺少：{}", 
                                    material.getMaterialCode(), material.getRequiredQuantity(),
                                    material.getAvailableQuantity(), material.getShortageQuantity());
                            }
                        }
                    } else {
                        log.error("出库单 {} 分配方案生成失败：{}", id, allocationResult.getMessage());
                    }
                } catch (Exception e) {
                    log.error("出库单 {} 自动分配方案失败", id, e);
                    // 分配失败不影响审核结果，但需要记录日志
                }
                
                // 审核通过时，需要更新关联的成品销售订单状态为6（待领料）
                String orderId = outbound.getOrderId();
                if (orderId != null && !orderId.isEmpty()) {
                    try {
                        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
                        // 更新订单状态为 领料单待审核5 -> 待领料6
                        boolean success = productSalesOrderService.updateOrderStatus(orderId, 5, 6, loginUser.getId(), "设计人员提交审核");
                        log.info("出库单审核通过，已更新订单状态：outboundId={}, orderId={}, status=6", id, orderId);
                    } catch (Exception e) {
                        log.error("更新订单状态失败：outboundId={}, orderId={}", id, orderId, e);
                        // 订单状态更新失败不影响出库单审核结果
                    }
                }
            } else if (updateResult && !isPass) {
                // 审核不通过时，清除已有的分配方案
                try {
                    wmsOutboundAllocationPlanService.clearAllocationPlansAndReleaseInventory(id);
                    log.info("出库单 {} 审核不通过，已清除分配方案", id);
                } catch (Exception e) {
                    log.error("清除出库单 {} 分配方案失败", id, e);
                }
            }
            
            return updateResult;
        } catch (Exception e) {
            log.error("出库单审核失败：outboundId={}, isPass={}", id, isPass, e);
            throw e;
        }
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean execute(String id) {
        WmsOutbound outbound = getById(id);
        // 只有已审核状态才能执行出库
        if (outbound == null || !"2".equals(outbound.getStatus())) {
            return false;
        }
        
        // 更新主表状态为已出库
        WmsOutbound updateOutbound = new WmsOutbound();
        updateOutbound.setId(id);
        updateOutbound.setStatus("3");
        boolean result = updateById(updateOutbound);
        
        if (result) {
            // 更新明细状态为已出库
            List<WmsOutboundDetail> detailList = wmsOutboundDetailService.selectByOutboundId(id);
            for (WmsOutboundDetail detail : detailList) {
                detail.setStatus("1");
                
                // 更新库存
                updateInventory(outbound, detail);
            }
            wmsOutboundDetailService.updateBatchById(detailList);
        }
        
        return result;
    }
    
    /**
     * 更新库存
     * 
     * @param outbound 出库单
     * @param detail 出库明细
     */
    private void updateInventory(WmsOutbound outbound, WmsOutboundDetail detail) {
        // 查询库存
        LambdaQueryWrapper<WmsInventory> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(WmsInventory::getMaterialCode, detail.getMaterialCode())
                    .eq(WmsInventory::getWarehouseId, detail.getWarehouseId());
        if (detail.getLocationId() != null && !detail.getLocationId().isEmpty()) {
            queryWrapper.eq(WmsInventory::getLocationId, detail.getLocationId());
        }
        if (detail.getBatchNo() != null && !detail.getBatchNo().isEmpty()) {
            queryWrapper.eq(WmsInventory::getBatchNo, detail.getBatchNo());
        }
        
        WmsInventory inventory = wmsInventoryService.getOne(queryWrapper);
        if (inventory != null) {
            // 减少库存
            BigDecimal newQuantity = inventory.getQuantity().subtract(detail.getQuantity());
            if (newQuantity.compareTo(BigDecimal.ZERO) < 0) {
                // 库存不足，设置为0
                newQuantity = BigDecimal.ZERO;
            }
            inventory.setQuantity(newQuantity);
            wmsInventoryService.updateById(inventory);
            
            // 记录库存历史
            WmsInventoryHistory history = new WmsInventoryHistory();
            history.setMaterialCode(detail.getMaterialCode());
            history.setMaterialName(detail.getMaterialName());
            history.setWarehouseId(detail.getWarehouseId());
            history.setLocationId(detail.getLocationId());
            history.setBatchNo(detail.getBatchNo());
            history.setBeforeQuantity(inventory.getQuantity());
            history.setChangeQuantity(detail.getQuantity().negate());
            history.setAfterQuantity(inventory.getQuantity().add(detail.getQuantity()));
            history.setRemark("出库单出库");
            wmsInventoryHistoryService.save(history);
        }
    }
    
    @Override
    public boolean uploadVoucher(String id, MultipartFile file) {
        if (file == null || file.isEmpty()) {
            return false;
        }
        
        try {
            // 获取文件名
            String fileName = file.getOriginalFilename();
            // 文件后缀
            String suffix = fileName.substring(fileName.lastIndexOf("."));
            // 生成新文件名
            String newFileName = UUID.randomUUID().toString().replace("-", "") + suffix;
            // 文件存储路径
            String filePath = uploadPath + File.separator + "outbound" + File.separator + "voucher";
            // 确保目录存在
            File dir = new File(filePath);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            // 文件全路径
            String fullPath = filePath + File.separator + newFileName;
            // 保存文件
            File dest = new File(fullPath);
            file.transferTo(dest);
            
            // 获取访问路径
            String accessPath = "/upload/outbound/voucher/" + newFileName;
            
            // 更新出库单凭证路径
            WmsOutbound updateOutbound = new WmsOutbound();
            updateOutbound.setId(id);
            updateOutbound.setOutboundVoucher(accessPath);
            return updateById(updateOutbound);
        } catch (IOException e) {
            log.error("上传出库凭证失败", e);
            return false;
        }
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean createOutboundFromRequisition(String requisitionId, String requisitionNo, String orderId, String orderNo, 
                                               String warehouseId, List<RequisitionOutboundEvent.ProfileItem> profileList, 
                                               List<RequisitionOutboundEvent.AccessoryItem> accessoryList, String tenantId) {
        try {
            log.info("开始根据领料单事件[{}]创建出库单", requisitionNo);
            
            // 创建出库单主表
            WmsOutbound outbound = new WmsOutbound();
            String dateStr = String.format("%tY%<tm%<td", new Date());
            String timeStr = String.format("%tH%<tM%<tS", new Date());
            String outboundNo = "SCLL-CK-" + dateStr + "-" + timeStr;
            outbound.setOutboundNo(outboundNo);
            outbound.setOutboundType("2"); // 生产领料
            outbound.setOutboundDate(new Date());
            outbound.setStatus("1"); // 待审核状态
            outbound.setWarehouseId(warehouseId);
            outbound.setOrderId(orderId);
            outbound.setOrderNo(orderNo);
            outbound.setRequisitionId(requisitionId);
            outbound.setRequisitionNo(requisitionNo);
            outbound.setRemark("根据领料单: "+requisitionNo+" 自动生成");
            outbound.setTenantId(tenantId);
            outbound.setCreateTime(new Date());
            
            // 保存出库单主表
            boolean result = save(outbound);
            if (!result) {
                log.error("保存出库单主表失败");
                return false;
            }
            
            // 创建出库明细列表
            List<WmsOutboundDetail> detailList = new java.util.ArrayList<>();
            
            // 处理型材数据
            if (profileList != null && !profileList.isEmpty()) {
                for (RequisitionOutboundEvent.ProfileItem profile : profileList) {
                    WmsOutboundDetail detail = new WmsOutboundDetail();
                    detail.setOutboundId(outbound.getId());
                    detail.setMaterialType("1"); // 型材
                    detail.setMaterialCode(profile.getProfileCode());
                    detail.setMaterialName(profile.getProfileName());
                    detail.setSpecification(profile.getLength() + "/-" + (profile.getWidth() != null ? profile.getWidth() : "") + "/-" + (profile.getHeight() != null ? profile.getHeight() : ""));
                    detail.setColor(profile.getColor());
                    detail.setQuantity(BigDecimal.valueOf(profile.getQuantity()));
                    detail.setUnit("根");
                    detail.setTenantId(tenantId);
                    detail.setCreateTime(new Date());
                    detailList.add(detail);
                }
            }
            
            // 处理配件数据
            if (accessoryList != null && !accessoryList.isEmpty()) {
                for (RequisitionOutboundEvent.AccessoryItem accessory : accessoryList) {
                    WmsOutboundDetail detail = new WmsOutboundDetail();
                    detail.setOutboundId(outbound.getId());
                    detail.setMaterialType("2"); // 配件
                    detail.setMaterialCode(accessory.getAccessoryCode());
                    detail.setMaterialName(accessory.getAccessoryName());
                    detail.setSpecification(accessory.getSpecification());
                    //对accessory.getQuantity()进行非空判断
                    if(accessory.getQuantity() != null && !accessory.getQuantity().equals("")){
                        detail.setQuantity(BigDecimal.valueOf(accessory.getQuantity()));
                    } else if (accessory.getLengthQuantity() != null && !accessory.getLengthQuantity().equals("")) {
                        detail.setQuantity(BigDecimal.valueOf(Double.valueOf(accessory.getLengthQuantity())));
                    }
                    detail.setUnit(accessory.getUnit());
                    detail.setRemark("长度数量: " + accessory.getLengthQuantity());
                    detail.setTenantId(tenantId);
                    detail.setCreateTime(new Date());
                    detailList.add(detail);
                }
            }
            
            // 保存出库明细
            if (!detailList.isEmpty()) {
                result = wmsOutboundDetailService.saveBatch(outbound.getId(), detailList);
                if (!result) {
                    log.error("保存出库单明细失败");
                    return false;
                }
            }
            
            log.info("成功根据领料单事件[{}]创建出库单[{}]，共{}条明细", requisitionNo, outboundNo, detailList.size());
            return true;
            
        } catch (Exception e) {
            log.error("根据领料单事件创建出库单失败", e);
            return false;
        }
    }
    
    /**
     * 根据物料信息生成出库明细汇总
     * 
     * @param outboundDetailVOList 前端传入的出库明细VO列表（物料信息和出库数量）
     * @return 生成的出库明细列表
     */
    private List<WmsOutboundDetail> generateOutboundDetailsFromPlan(List<org.jeecg.modules.wms.vo.WmsOutboundDetailVO> outboundDetailVOList) {
        List<WmsOutboundDetail> detailList = new java.util.ArrayList<>();
        
        for (org.jeecg.modules.wms.vo.WmsOutboundDetailVO detailVO : outboundDetailVOList) {
            // 创建汇总明细记录，保存物料信息和总出库数量
            WmsOutboundDetail detail = new WmsOutboundDetail();
            
            // 设置物料基本信息
            detail.setMaterialType(detailVO.getMaterialType());
            detail.setMaterialCode(detailVO.getMaterialCode());
            detail.setMaterialName(detailVO.getMaterialName());
            detail.setSpecification(detailVO.getSpecification());
            detail.setColor(detailVO.getColor());
            detail.setUnit(detailVO.getUnit());
            detail.setQuantity(detailVO.getQuantity());
            detail.setRemark(detailVO.getRemark());
            
            // 设置初始状态为待出库
            detail.setStatus("0");
            
            // 仓库和库位信息在审核分配时再确定，这里暂时为空
            detail.setWarehouseId(null);
            detail.setWarehouseName(null);
            detail.setLocationId(null);
            detail.setLocationName(null);
            
            detailList.add(detail);
        }
        
        log.info("生成出库明细汇总：共{}条明细", detailList.size());
        return detailList;
    }
    
    /**
     * 更新出库明细的分配状态
     * @param outboundId 出库单ID
     * @param allocationResult 分配结果
     */
    private void updateOutboundDetailAllocationStatus(String outboundId, OutboundAllocationResult allocationResult) {
        try {
            // 获取出库明细列表
            List<WmsOutboundDetail> outboundDetails = wmsOutboundDetailService.selectByOutboundId(outboundId);
            if (outboundDetails == null || outboundDetails.isEmpty()) {
                return;
            }
            
            // 创建物料分配信息映射
            Map<String, MaterialAllocationInfo> materialAllocationMap = new HashMap<>();
            
            // 处理已分配的物料
            if (allocationResult.getAllocationPlans() != null) {
                for (WmsOutboundAllocationPlan plan : allocationResult.getAllocationPlans()) {
                    String materialCode = plan.getMaterialCode();
                    MaterialAllocationInfo info = materialAllocationMap.getOrDefault(materialCode, new MaterialAllocationInfo());
                    info.allocatedQuantity = info.allocatedQuantity.add(plan.getAllocationQuantity());
                    materialAllocationMap.put(materialCode, info);
                }
            }
            
            // 处理未分配的物料
            if (allocationResult.getUnallocatedMaterials() != null) {
                for (OutboundAllocationResult.UnallocatedMaterial material : allocationResult.getUnallocatedMaterials()) {
                    String materialCode = material.getMaterialCode();
                    MaterialAllocationInfo info = materialAllocationMap.getOrDefault(materialCode, new MaterialAllocationInfo());
                    info.shortageQuantity = material.getShortageQuantity();
                    info.availableQuantity = material.getAvailableQuantity();
                    materialAllocationMap.put(materialCode, info);
                }
            }
            
            // 更新出库明细的分配状态和数量信息
            for (WmsOutboundDetail detail : outboundDetails) {
                String materialCode = detail.getMaterialCode();
                MaterialAllocationInfo info = materialAllocationMap.get(materialCode);
                
                WmsOutboundDetail updateDetail = new WmsOutboundDetail();
                updateDetail.setId(detail.getId());
                
                if (info != null) {
                    // 设置已分配数量
                    updateDetail.setAllocatedQuantity(info.allocatedQuantity);
                    
                    // 计算剩余待分配数量
                    BigDecimal remainingQuantity = detail.getQuantity().subtract(info.allocatedQuantity);
                    updateDetail.setRemainingQuantity(remainingQuantity.max(BigDecimal.ZERO));
                    
                    // 确定分配状态
                    String allocationStatus;
                    if (remainingQuantity.compareTo(BigDecimal.ZERO) <= 0) {
                        allocationStatus = "1"; // 已完全分配
                    } else if (info.allocatedQuantity.compareTo(BigDecimal.ZERO) > 0) {
                        allocationStatus = "2"; // 部分分配
                    } else {
                        allocationStatus = "3"; // 未分配
                    }
                    updateDetail.setAllocationStatus(allocationStatus);
                    
                    // 根据分配状态更新备注
                    String statusRemark = "";
                    switch (allocationStatus) {
                        case "1":
                            statusRemark = "已完全分配";
                            break;
                        case "2":
                            statusRemark = String.format("部分分配，已分配：%s，剩余：%s", 
                                info.allocatedQuantity, remainingQuantity);
                            break;
                        case "3":
                            statusRemark = "未分配，库存不足";
                            break;
                    }
                    
                    // 在原有备注基础上添加分配状态信息
                    String originalRemark = detail.getRemark();
                    if (oConvertUtils.isNotEmpty(originalRemark)) {
                        updateDetail.setRemark(originalRemark + ";" + statusRemark);
                    } else {
                        updateDetail.setRemark(statusRemark);
                    }
                } else {
                    // 没有分配信息，设置为已完全分配（默认情况）
                    updateDetail.setAllocatedQuantity(detail.getQuantity());
                    updateDetail.setRemainingQuantity(BigDecimal.ZERO);
                    updateDetail.setAllocationStatus("1");
                    updateDetail.setRemark(detail.getRemark() + ";已完全分配");
                }
                
                wmsOutboundDetailService.updateById(updateDetail);
            }
            
            log.info("出库单 {} 明细分配状态更新完成", outboundId);
            
        } catch (Exception e) {
            log.error("更新出库单 {} 明细分配状态失败", outboundId, e);
        }
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean oneClickOutbound(String id) {
        try {
            log.info("开始执行一键出库，出库单ID: {}", id);
            
            // 1. 查询出库单信息
            WmsOutbound outbound = this.getById(id);
            if (outbound == null) {
                log.error("出库单不存在: {}", id);
                throw new RuntimeException("出库单不存在");
            }
            
            // 2. 验证出库单状态（只有已审核2和部分出库5的状态可以执行一键出库）
            if (!"2".equals(outbound.getStatus()) && !"5".equals(outbound.getStatus())) {
                log.error("出库单状态不允许执行一键出库操作: 出库单ID={}, 状态={}", id, outbound.getStatus());
                throw new RuntimeException("出库单状态不允许执行一键出库操作");
            }
            
            // 3. 查询待出库、部分出库的明细列表
            LambdaQueryWrapper<WmsOutboundDetail> detailQuery = new LambdaQueryWrapper<>();
            detailQuery.eq(WmsOutboundDetail::getOutboundId, id)
                    .ne(WmsOutboundDetail::getStatus,1);
            List<WmsOutboundDetail> outboundDetails = wmsOutboundDetailService.list(detailQuery);
            
            if (outboundDetails.isEmpty()) {
                log.error("出库单无明细数据: {}", id);
                throw new RuntimeException("出库单无明细数据");
            }
            
            // 4. 查询未出库的分配方案
            LambdaQueryWrapper<WmsOutboundAllocationPlan> planQuery = new LambdaQueryWrapper<>();
            planQuery.eq(WmsOutboundAllocationPlan::getOutboundId, id)
                    .ne(WmsOutboundAllocationPlan::getStatus,1);
            List<WmsOutboundAllocationPlan> allocationPlans = wmsOutboundAllocationPlanService.list(planQuery);
            
            if (allocationPlans.isEmpty()) {
                log.error("出库单无分配方案: {}", id);
                throw new RuntimeException("出库单无分配方案，请先审核生成分配方案");
            }
            
            // 5. 按出库明细分组处理分配方案
            Map<String, List<WmsOutboundAllocationPlan>> plansByDetail = allocationPlans.stream()
                .collect(Collectors.groupingBy(plan -> 
                    plan.getMaterialCode() + "_" + (plan.getSpecification() != null ? plan.getSpecification() : "") + "_" + (plan.getColor() != null ? plan.getColor() : "")));
            
            // 6. 处理每个出库明细的出库操作
            for (WmsOutboundDetail detail : outboundDetails) {
                // 只处理已分配(1)和部分分配(2)的明细
                if (!"1".equals(detail.getAllocationStatus()) && !"2".equals(detail.getAllocationStatus())) {
                    continue;
                }
                
                String detailKey = detail.getMaterialCode() + "_" + (detail.getSpecification() != null ? detail.getSpecification() : "") + "_" + (detail.getColor() != null ? detail.getColor() : "");
                List<WmsOutboundAllocationPlan> detailPlans = plansByDetail.get(detailKey);
                
                if (detailPlans == null || detailPlans.isEmpty()) {
                    continue;
                }

                
                // 执行库存出库操作
                for (WmsOutboundAllocationPlan plan : detailPlans) {

                    // 计算实际出库数量（取分配数量）
                    BigDecimal actualOutboundQuantity = plan.getAllocationQuantity();
                    if (actualOutboundQuantity.compareTo(BigDecimal.ZERO) <= 0) {
                        break;
                    }
                    //若实际出库数量= 需求数量  则修改plan的状态为1已出库
                    if(actualOutboundQuantity.compareTo(plan.getRequiredQuantity())==0){
                        // 更新分配方案状态为已出库
                        plan.setStatus(1);
                        plan.setActualQuantity(actualOutboundQuantity);
                        plan.setOutboundTime(new Date());
                        wmsOutboundAllocationPlanService.updateById(plan);
                    }
                    
                    // 获取库存记录
                    WmsInventory inventory = wmsInventoryService.getById(plan.getInventoryId());
                    if (inventory == null) {
                        log.warn("库存记录不存在: {}", plan.getInventoryId());
                        continue;
                    }
                    
                    // 更新库存数据
                    updateInventoryForOutbound(inventory, actualOutboundQuantity);
                    
                    // 记录库存历史
                    recordInventoryHistory(inventory, actualOutboundQuantity, "一键出库", outbound.getOutboundNo(),outbound.getOutboundType());
                    
                    log.info("库存出库成功: 库存ID={}, 出库数量={}", inventory.getId(), actualOutboundQuantity);
                }
                
                // 更新出库明细状态
                updateOutboundDetailStatus(detail);
            }
            
            // 7. 更新出库单状态
            updateOutboundStatus(outbound);
            return true;
            
        } catch (Exception e) {
            log.error("一键出库失败: 出库单ID={}", id, e);
            throw new RuntimeException("一键出库失败: " + e.getMessage());
        }
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean oneClickOutboundForMobile(String id, List<String> planIdList) {
        try {
            log.info("开始执行移动端一键出库，出库单ID: {}, 分配方案ID列表: {}", id, planIdList);
            
            if (planIdList == null || planIdList.isEmpty()) {
                log.error("分配方案ID列表为空");
                throw new RuntimeException("请选择要出库的分配方案");
            }
            
            // 1. 查询出库单信息
            WmsOutbound outbound = this.getById(id);
            if (outbound == null) {
                log.error("出库单不存在: {}", id);
                throw new RuntimeException("出库单不存在");
            }
            
            // 2. 验证出库单状态（只有已审核2和部分出库5的状态可以执行出库）
            if (!"2".equals(outbound.getStatus()) && !"5".equals(outbound.getStatus())) {
                log.error("出库单状态不允许执行出库操作: 出库单ID={}, 状态={}", id, outbound.getStatus());
                throw new RuntimeException("出库单状态不允许执行出库操作");
            }
            
            // 3. 查询指定的分配方案
            LambdaQueryWrapper<WmsOutboundAllocationPlan> planQuery = new LambdaQueryWrapper<>();
            planQuery.eq(WmsOutboundAllocationPlan::getOutboundId, id)
                    .in(WmsOutboundAllocationPlan::getId, planIdList)
                    .ne(WmsOutboundAllocationPlan::getStatus, 1); // 排除已出库的
            List<WmsOutboundAllocationPlan> selectedPlans = wmsOutboundAllocationPlanService.list(planQuery);
            
            if (selectedPlans.isEmpty()) {
                log.error("未找到可出库的分配方案: 出库单ID={}, 方案ID列表={}", id, planIdList);
                throw new RuntimeException("未找到可出库的分配方案");
            }
            
            // 4. 按物料分组处理分配方案
            Map<String, List<WmsOutboundAllocationPlan>> plansByMaterial = selectedPlans.stream()
                .collect(Collectors.groupingBy(plan -> 
                    plan.getMaterialCode() + "_" + (plan.getSpecification() != null ? plan.getSpecification() : "") + "_" + (plan.getColor() != null ? plan.getColor() : "")));
            
            // 5. 查询相关的出库明细
            LambdaQueryWrapper<WmsOutboundDetail> detailQuery = new LambdaQueryWrapper<>();
            detailQuery.eq(WmsOutboundDetail::getOutboundId, id);
            List<WmsOutboundDetail> outboundDetails = wmsOutboundDetailService.list(detailQuery);
            
            // 6. 执行选中分配方案的出库操作
            for (WmsOutboundAllocationPlan plan : selectedPlans) {
                // 计算实际出库数量（取分配数量）
                BigDecimal actualOutboundQuantity = plan.getAllocationQuantity();
                if (actualOutboundQuantity.compareTo(BigDecimal.ZERO) <= 0) {
                    log.warn("分配方案数量为0，跳过: 方案ID={}", plan.getId());
                    continue;
                }
                
                // 获取库存记录
                WmsInventory inventory = wmsInventoryService.getById(plan.getInventoryId());
                if (inventory == null) {
                    log.warn("库存记录不存在: {}", plan.getInventoryId());
                    continue;
                }
                
                // 检查库存数量是否足够
                if (inventory.getQuantity().compareTo(actualOutboundQuantity) < 0) {
                    log.error("库存数量不足: 库存ID={}, 当前数量={}, 需要数量={}", 
                        inventory.getId(), inventory.getQuantity(), actualOutboundQuantity);
                    throw new RuntimeException("库存数量不足，无法完成出库操作");
                }
                
                // 更新库存数据
                updateInventoryForOutbound(inventory, actualOutboundQuantity);
                
                // 记录库存历史
                recordInventoryHistory(inventory, actualOutboundQuantity, "移动端出库", outbound.getOutboundNo(), outbound.getOutboundType());
                
                // 更新分配方案状态为已出库
                plan.setStatus(1);
                plan.setActualQuantity(actualOutboundQuantity);
                plan.setOutboundTime(new Date());
                wmsOutboundAllocationPlanService.updateById(plan);
                
                log.info("分配方案出库成功: 方案ID={}, 库存ID={}, 出库数量={}", 
                    plan.getId(), inventory.getId(), actualOutboundQuantity);
            }
            
            // 7. 更新相关出库明细的状态
            for (WmsOutboundDetail detail : outboundDetails) {
                String detailKey = detail.getMaterialCode() + "_" + (detail.getSpecification() != null ? detail.getSpecification() : "") + "_" + (detail.getColor() != null ? detail.getColor() : "");
                List<WmsOutboundAllocationPlan> detailPlans = plansByMaterial.get(detailKey);
                
                if (detailPlans != null && !detailPlans.isEmpty()) {
                    // 检查该明细的所有分配方案是否都已出库
                    LambdaQueryWrapper<WmsOutboundAllocationPlan> allPlansQuery = new LambdaQueryWrapper<>();
                    allPlansQuery.eq(WmsOutboundAllocationPlan::getOutboundId, id)
                            .eq(WmsOutboundAllocationPlan::getMaterialCode, detail.getMaterialCode())
                            .eq(WmsOutboundAllocationPlan::getSpecification, detail.getSpecification())
                            .eq(WmsOutboundAllocationPlan::getColor, detail.getColor());
                    List<WmsOutboundAllocationPlan> allDetailPlans = wmsOutboundAllocationPlanService.list(allPlansQuery);
                    
                    // 计算已出库数量
                    BigDecimal outboundedQuantity = allDetailPlans.stream()
                        .filter(p -> 1 == p.getStatus())
                        .map(WmsOutboundAllocationPlan::getActualQuantity)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                    
                    // 更新明细状态
                    if (outboundedQuantity.compareTo(detail.getQuantity()) >= 0) {
                        // 全部出库
                        detail.setStatus("1");
                        detail.setAllocationStatus("3"); // 已分配
                    } else if (outboundedQuantity.compareTo(BigDecimal.ZERO) > 0) {
                        // 部分出库
                        detail.setStatus("2");
                        detail.setAllocationStatus("2"); // 部分分配
                    }
                    
                    wmsOutboundDetailService.updateById(detail);
                }
            }
            
            // 8. 更新出库单状态
            updateOutboundStatus(outbound);
            
            log.info("移动端一键出库完成: 出库单ID={}, 处理分配方案数量={}", id, selectedPlans.size());
            return true;
            
        } catch (Exception e) {
            log.error("移动端一键出库失败: 出库单ID={}, 分配方案ID列表={}", id, planIdList, e);
            throw new RuntimeException("移动端一键出库失败: " + e.getMessage());
        }
    }
    
    /**
     * 更新库存数据（出库操作）
     */
    private void updateInventoryForOutbound(WmsInventory inventory, BigDecimal outboundQuantity) {
        // 减少预出库数量
        BigDecimal newPreOutboundQuantity = inventory.getPreOutboundQuantity().subtract(outboundQuantity);
        // 减少库存数量
        BigDecimal newQuantity = inventory.getQuantity().subtract(outboundQuantity);


        //根据出库后剩余库存数量修改库位状态 为库存为0，则删除库存信息并释放库位
        if(newQuantity.compareTo(BigDecimal.ZERO)==0){
            //释放库位
            locationService.update(new UpdateWrapper<WmsWarehouseLocation>()
                    .eq("id", inventory.getLocationId())
                    .set("status", "1"));
            //删除库存信息
            wmsInventoryService.removeById(inventory.getId());
        }else {
            WmsInventory updateInventory = new WmsInventory();
            updateInventory.setId(inventory.getId());
            updateInventory.setQuantity(newQuantity);
            updateInventory.setPreOutboundQuantity(newPreOutboundQuantity);
            updateInventory.setUpdateTime(new Date());
            boolean updated = wmsInventoryService.updateById(updateInventory);
            //修改库位为未满
            locationService.update(new UpdateWrapper<WmsWarehouseLocation>()
                    .eq("id", inventory.getLocationId())
                    .set("status", "2"));
            if (!updated) {
                throw new RuntimeException("更新库存失败: 库存ID=" + inventory.getId());
            }
        }
    }
    
    /**
     * 记录库存历史
     */
    private void recordInventoryHistory(WmsInventory inventory, BigDecimal outboundQuantity, String operation, String outboundNo, String outboundType) {
        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        WmsInventoryHistory history = new WmsInventoryHistory();
        history.setInventoryId(inventory.getId());
        history.setMaterialCode(inventory.getMaterialCode());
        history.setMaterialName(inventory.getMaterialName());
        history.setWarehouseId(inventory.getWarehouseId());
        history.setWarehouseName(inventory.getWarehouseName());
        history.setLocationId(inventory.getLocationId());
        history.setLocationName(inventory.getLocationName());
        history.setBatchNo(inventory.getBatchNo());
        history.setOperateTime(new Date());
        history.setOperator(loginUser.getRealname());
        history.setColor(inventory.getColor());
        history.setMaterialSeries(inventory.getMaterialSeries());
        history.setMaterialType(inventory.getMaterialType());
        history.setBusinessNo(outboundNo);

        if(oConvertUtils.isNotEmpty(outboundType)){
            switch (outboundType){
                case "1":
                    history.setBusinessType(23); // 销售出库
                    break;
                case "2":
                    history.setBusinessType(21); // 生产领料
                    break;
                case "3":
                    history.setBusinessType(22); // 调拨出库
                    break;
                case "4":
                    history.setBusinessType(29); // 其他出库
            }
        }
        history.setBeforeQuantity(inventory.getQuantity());
        history.setChangeQuantity(outboundQuantity.negate()); // 出库为负数
        history.setAfterQuantity(inventory.getQuantity().subtract(outboundQuantity));
        history.setRemark(outboundNo +" " + DateUtils.formatDateTime() + " " + operation);
        wmsInventoryHistoryService.save(history);
    }
    
    /**
     * 更新出库明细状态   此次出库时 该明细的剩余分配数=0代表这此次出库是该明细的最后一次出库
     */
    private void updateOutboundDetailStatus(WmsOutboundDetail detail) {
        String newStatus;
        if (detail.getRemainingQuantity().compareTo(BigDecimal.ZERO) <= 0) {
            newStatus = "1"; // 全部出库
        } else {
            newStatus = "2"; // 部分出库
        }
        
        WmsOutboundDetail updateDetail = new WmsOutboundDetail();
        updateDetail.setId(detail.getId());
        updateDetail.setStatus(newStatus);
        updateDetail.setUpdateTime(new Date());
        
        boolean updated = wmsOutboundDetailService.updateById(updateDetail);
        if (!updated) {
            throw new RuntimeException("更新出库明细状态失败: 明细ID=" + detail.getId());
        }
    }
    
    /**
     * 更新出库单状态
     */
    private void updateOutboundStatus(WmsOutbound outbound) {


        WmsOutbound updateOutbound = new WmsOutbound();
        updateOutbound.setId(outbound.getId());
        updateOutbound.setUpdateTime(new Date());


        //出库单状态根据 下属所有出库明细的出库状态判断
        long count = wmsOutboundDetailService.count(new LambdaQueryWrapper<WmsOutboundDetail>()
                .eq(WmsOutboundDetail::getOutboundId,outbound.getId())
                .ne(WmsOutboundDetail::getStatus,1));
        if(count != 0){
            updateOutbound.setStatus("5");
        }else {
            updateOutbound.setStatus("3");
        }

        //更新出库单操作人信息
        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        //需要判断操作人中是否已存在当前用户
        if(oConvertUtils.isNotEmpty(outbound.getOperatorIds())){
            if( !outbound.getOperatorIds().contains(loginUser.getId())){
                updateOutbound.setOperatorIds( outbound.getOperatorIds()+ "," +loginUser.getId() );
            }
        }else {
            updateOutbound.setOperatorIds(loginUser.getId());
        }
        if(oConvertUtils.isNotEmpty(outbound.getOperatorNames())){
            if( !outbound.getOperatorNames().contains(loginUser.getRealname())){
                updateOutbound.setOperatorNames( outbound.getOperatorNames()+ "," +loginUser.getRealname() );
            }
        }else {
            updateOutbound.setOperatorNames(loginUser.getRealname());
        }
        
        boolean updated = this.updateById(updateOutbound);
        if (!updated) {
            throw new RuntimeException("更新出库单状态失败: 出库单ID=" + outbound.getId());
        }
        
        // 如果出库单完成（状态为3）且为生产领料出库（业务类型为2），自动生成余料出库单
        if ("3".equals(updateOutbound.getStatus()) && "2".equals(outbound.getOutboundType())) {
            handleSurplusInboundGeneration(outbound);
        }
    }
    
    @Override
    public IPage<WmsOutboundVO> getUserOperationRecords(Page<WmsOutboundVO> page, WmsOutbound queryParam,
                                                       String planOutboundStartTime, String planOutboundEndTime,
                                                       String actualOutboundStartTime, String actualOutboundEndTime,
                                                       String warehouseName, String locationName,
                                                       String materialCode, String materialName,
                                                        List<String> statusList, List<String> businessTypeList) {
        try {
            // 获取当前用户信息
            LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
            if (loginUser == null) {
                throw new RuntimeException("用户未登录");
            }
            
            String userId = loginUser.getId();
            log.info("查询用户出库操作记录，用户ID: {}, 筛选条件: {}", userId, queryParam);
            
            // 构建查询条件
            LambdaQueryWrapper<WmsOutbound> queryWrapper = new LambdaQueryWrapper<>();
            
            // 用户权限过滤
            queryWrapper.like(WmsOutbound::getOperatorIds, userId);
            
            // 添加筛选条件
            if (queryParam != null) {
                if (StringUtils.isNotBlank(queryParam.getOutboundNo())) {
                    queryWrapper.like(WmsOutbound::getOutboundNo, queryParam.getOutboundNo());
                }
                if (statusList != null) {
                    queryWrapper.in(WmsOutbound::getStatus, statusList);
                }
                if (businessTypeList != null) {
                    queryWrapper.in(WmsOutbound::getOutboundType, businessTypeList);
                }
            }
            
            // 时间范围筛选
            if (StringUtils.isNotBlank(planOutboundStartTime)) {
                queryWrapper.ge(WmsOutbound::getOutboundDate, planOutboundStartTime);
            }
            if (StringUtils.isNotBlank(planOutboundEndTime)) {
                queryWrapper.le(WmsOutbound::getOutboundDate, planOutboundEndTime);
            }
            if (StringUtils.isNotBlank(actualOutboundStartTime)) {
                queryWrapper.ge(WmsOutbound::getActualOutboundDate, actualOutboundStartTime);
            }
            if (StringUtils.isNotBlank(actualOutboundEndTime)) {
                queryWrapper.le(WmsOutbound::getActualOutboundDate, actualOutboundEndTime);
            }
            
            queryWrapper.orderByDesc(WmsOutbound::getUpdateTime);
            
            Page<WmsOutbound> entityPage = new Page<>(page.getCurrent(), page.getSize());
            IPage<WmsOutbound> outboundPage = this.page(entityPage, queryWrapper);
            
            // 转换为VO对象
            List<WmsOutboundVO> voList = new ArrayList<>();
            for (WmsOutbound outbound : outboundPage.getRecords()) {
                WmsOutboundVO vo = this.queryByIdAnUpdateBy(outbound.getId(), loginUser.getUsername());
                if (vo != null) {
                    // 如果有仓库、库位、物料筛选条件，需要进一步过滤
                    if (shouldIncludeOutboundRecord(vo, warehouseName, locationName, materialCode, materialName)) {
                        voList.add(vo);
                    }
                }
            }
            
            // 构建返回结果
            Page<WmsOutboundVO> resultPage = new Page<>(page.getCurrent(), page.getSize(), outboundPage.getTotal());
            resultPage.setRecords(voList);
            
            log.info("查询用户出库操作记录完成，返回 {} 条记录", voList.size());
            return resultPage;
            
        } catch (Exception e) {
            log.error("查询用户出库操作记录失败", e);
            throw new RuntimeException("查询用户出库操作记录失败: " + e.getMessage());
        }
    }
    
    /**
     * 检查出库记录是否符合筛选条件
     */
    private boolean shouldIncludeOutboundRecord(WmsOutboundVO vo, String warehouseName, String locationName, 
                                              String materialCode, String materialName) {
        // 如果没有明细相关的筛选条件，直接返回true
        if (StringUtils.isBlank(warehouseName) && StringUtils.isBlank(locationName) && 
            StringUtils.isBlank(materialCode) && StringUtils.isBlank(materialName)) {
            return true;
        }
        
        // 检查仓库名称
        if (StringUtils.isNotBlank(warehouseName) && 
            (vo.getWarehouseName() == null || !vo.getWarehouseName().contains(warehouseName))) {
            return false;
        }
        
        // 检查分配方案中的筛选条件
        if (vo.getAllocationPlanList() != null && !vo.getAllocationPlanList().isEmpty()) {
            for (Object plan : vo.getAllocationPlanList()) {
                if (plan instanceof Map) {
                    Map<String, Object> planMap = (Map<String, Object>) plan;
                    
                    // 检查库位名称
                    if (StringUtils.isNotBlank(locationName)) {
                        String planLocationName = (String) planMap.get("locationName");
                        if (planLocationName != null && planLocationName.contains(locationName)) {
                            return true;
                        }
                    }
                    
                    // 检查物料编码
                    if (StringUtils.isNotBlank(materialCode)) {
                        String planMaterialCode = (String) planMap.get("materialCode");
                        if (planMaterialCode != null && planMaterialCode.contains(materialCode)) {
                            return true;
                        }
                    }
                    
                    // 检查物料名称
                    if (StringUtils.isNotBlank(materialName)) {
                        String planMaterialName = (String) planMap.get("materialName");
                        if (planMaterialName != null && planMaterialName.contains(materialName)) {
                            return true;
                        }
                    }
                }
            }
            
            // 如果有明细筛选条件但没有匹配的分配方案，返回false
            if (StringUtils.isNotBlank(locationName) || StringUtils.isNotBlank(materialCode) || 
                StringUtils.isNotBlank(materialName)) {
                return false;
            }
        }
        
        return true;
    }

    @Override
    public WmsOutboundVO queryByIdAnUpdateBy(String id, String username) {
        WmsOutboundVO wmsOutboundVO = baseMapper.queryOutboundById(id);
        if (wmsOutboundVO != null) {
            List<WmsOutboundAllocationPlan> allocationPlanList = wmsOutboundAllocationPlanService.selectByOutboundIdAndUpdateBy(id,username);

            wmsOutboundVO.setAllocationPlanList(allocationPlanList);
        }
        return wmsOutboundVO;
    }
    /**
     * 物料分配信息内部类
     */
    private static class MaterialAllocationInfo {
        BigDecimal allocatedQuantity = BigDecimal.ZERO;
        BigDecimal shortageQuantity = BigDecimal.ZERO;
        BigDecimal availableQuantity = BigDecimal.ZERO;
    }
    
    /**
     * 处理余料出库单自动生成
     * 
     * @param outbound 出库单信息
     */
    private void handleSurplusInboundGeneration(WmsOutbound outbound) {
        // 检查是否启用自动余料出库单生成功能
        if (!surplusInboundConfig.isEnabled()) {
            log.debug("自动余料出库单生成功能已禁用，跳过处理");
            return;
        }
        
        try {
            log.info("生产领料出库单[{}]完成，开始自动生成余料出库单", outbound.getOutboundNo());
            
            // 获取关联的生产订单ID
            String productionOrderId = outbound.getOrderId();
            if (StringUtils.isBlank(productionOrderId)) {
                log.warn("生产领料出库单[{}]未关联生产订单，跳过余料出库单生成", outbound.getOutboundNo());
                return;
            }
            
            // 重试机制
            int retryCount = 0;
            int maxRetries = surplusInboundConfig.getMaxRetryCount();
            long retryInterval = surplusInboundConfig.getRetryIntervalMs();
            
            while (retryCount <= maxRetries) {
                try {
                    // 调用自动创建余料出库单服务
                    String surplusInboundNo = wmsInboundOrderService.autoCreateSurplusInboundOrder(productionOrderId, outbound.getId());
                    
                    if (StringUtils.isNotBlank(surplusInboundNo)) {
                        log.info("生产订单[{}]自动生成余料出库单成功，入库单号: {}", productionOrderId, surplusInboundNo);
                    } else {
                        log.info("生产订单[{}]无有效余料，未生成余料出库单", productionOrderId);
                    }
                    
                    // 成功则跳出重试循环
                    break;
                    
                } catch (Exception e) {
                    retryCount++;
                    log.warn("自动生成余料出库单失败，第{}次重试，出库单: {}", retryCount, outbound.getOutboundNo(), e);
                    
                    if (retryCount > maxRetries) {
                        // 超过最大重试次数
                        String errorMsg = String.format("自动生成余料出库单失败，已重试%d次，出库单: %s", maxRetries, outbound.getOutboundNo());
                        log.error(errorMsg, e);
                        
                        // 根据配置决定是否阻止出库完成
                        if (surplusInboundConfig.isBlockOutboundOnFailure()) {
                            throw new RuntimeException("余料出库单生成失败，阻止出库完成: " + e.getMessage());
                        }
                        break;
                    }
                    
                    // 等待重试间隔
                    if (retryInterval > 0) {
                        try {
                            Thread.sleep(retryInterval);
                        } catch (InterruptedException ie) {
                            Thread.currentThread().interrupt();
                            log.warn("重试等待被中断");
                            break;
                        }
                    }
                }
            }
            
        } catch (Exception e) {
            String errorMsg = String.format("处理余料出库单生成时发生异常，出库单: %s", outbound.getOutboundNo());
            log.error(errorMsg, e);
            
            // 根据配置决定是否阻止出库完成
            if (surplusInboundConfig.isBlockOutboundOnFailure()) {
                throw new RuntimeException("余料出库单生成异常，阻止出库完成: " + e.getMessage());
            }
        }
    }



    @Override
    public List<Map<String, Object>> parseRemainingMaterialExcel(MultipartFile file) {
        List<Map<String, Object>> result = new ArrayList<>();

        try {
            Workbook workbook = null;
            String fileName = file.getOriginalFilename();

            // 根据文件扩展名创建对应的工作簿
            if (fileName.endsWith(".xlsx")) {
                workbook = new XSSFWorkbook(file.getInputStream());
            } else if (fileName.endsWith(".xls")) {
                workbook = new HSSFWorkbook(file.getInputStream());
            } else {
                throw new JeecgBootException("不支持的文件格式，请上传.xlsx或.xls文件");
            }

            // 获取第一个工作表
            Sheet sheet = workbook.getSheetAt(0);
            if (sheet == null) {
                throw new JeecgBootException("Excel文件中没有找到工作表");
            }

            // 获取表头行
            Row headerRow = sheet.getRow(0);
            if (headerRow == null) {
                throw new JeecgBootException("Excel文件中没有找到表头行");
            }


            // 检查表头行是否对照  出库单号领料人系列材料名称编码颜色颜色编码厚度框型编码产地支长支重仓位出库数量出库总量出库方式金额备注合同号
            //拼接表头行0-17列
            String headerRowString = "";
            for (int i = 0; i < headerRow.getLastCellNum(); i++) {
                headerRowString = headerRowString + headerRow.getCell(i).getStringCellValue();
            }
            if (!headerRowString.equals("出库单号领料人系列材料名称编码颜色颜色编码厚度框型编码产地支长支重仓位出库数量出库总量出库方式金额备注合同号")) {
                throw new JeecgBootException("Excel文件中的表头行与期望的列名不匹配");
            }

            // 定义期望的列名映射
            Map<String, String> columnMapping = new HashMap<>();
            columnMapping.put("出库单号", "outboundNo");
            columnMapping.put("系列", "series");
            columnMapping.put("材料名称", "materialName");
            columnMapping.put("编码", "materialCode");
            columnMapping.put("颜色", "materialColor");
            columnMapping.put("支长", "materialLength");
            columnMapping.put("出库数量", "outboundQuantity");
            columnMapping.put("合同号", "orderNo");
            columnMapping.put("出库方式", "unit");

            // 解析表头，建立列索引映射
            Map<String, Integer> columnIndexMap = new HashMap<>();
            for (int i = 0; i < headerRow.getLastCellNum(); i++) {
                Cell cell = headerRow.getCell(i);
                if (cell != null) {
                    String headerValue = getCellStringValue(cell).trim();
                    if (columnMapping.containsKey(headerValue)) {
                        columnIndexMap.put(columnMapping.get(headerValue), i);
                    }
                }
            }


            // 解析数据行
            for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
                Row row = sheet.getRow(rowIndex);
                if (row == null) {
                    continue;
                }

                // 检查是否为空行
                boolean isEmptyRow = true;
                for (int i = 0; i < row.getLastCellNum(); i++) {
                    Cell cell = row.getCell(i);
                    if (cell != null && StringUtils.isNotBlank(getCellStringValue(cell))) {
                        isEmptyRow = false;
                        break;
                    }
                }

                if (isEmptyRow) {
                    continue;
                }

                Map<String, Object> rowData = new HashMap<>();

                // 解析各列数据
                for (Map.Entry<String, Integer> entry : columnIndexMap.entrySet()) {
                    String fieldName = entry.getKey();
                    Integer columnIndex = entry.getValue();

                    Cell cell = row.getCell(columnIndex);
                    String cellValue = getCellStringValue(cell);

                    if ("outboundQuantity".equals(fieldName)) {
                        // 入库数量需要转换为数字
                        try {
                            if (StringUtils.isNotBlank(cellValue)) {
                                BigDecimal quantity = new BigDecimal(cellValue);
                                rowData.put(fieldName, quantity);
                            }
                        } catch (NumberFormatException e) {
                            log.warn("第{}行入库数量格式错误: {}", rowIndex + 1, cellValue);
                            rowData.put(fieldName, BigDecimal.ZERO);
                        }
                    } else {
                        rowData.put(fieldName, cellValue);
                    }
                }

                // 验证必需字段
                String materialCode = (String) rowData.get("materialCode");
                String materialName = (String) rowData.get("materialName");
                BigDecimal outboundQuantity = (BigDecimal) rowData.get("outboundQuantity");

                if (StringUtils.isBlank(materialCode) || StringUtils.isBlank(materialName) ||
                        outboundQuantity == null || outboundQuantity.compareTo(BigDecimal.ZERO) <= 0) {
                    continue;
                }

                //根据materialCode、系列、颜色、长度获取库存信息
                List<WmsInventory> inventoryList = wmsInventoryService.list(new QueryWrapper<WmsInventory>()
                        .eq("material_code", materialCode)
                        .eq("material_series",rowData.get("series"))
                        .eq("material_length", rowData.get("materialLength"))
                        .eq("color", rowData.get("materialColor")));

                if (inventoryList.size() > 0) {
                    WmsInventory inventory = inventoryList.get(0);
                    WmsWarehouseLocation location = locationService.getById(inventory.getLocationId());
                    rowData.put("locationId", location.getId());
                    rowData.put("locationName", location.getLocationName());
                    rowData.put("locationCode", location.getLocationCode());
                    rowData.put("warehouseId", location.getWarehouseId());
                    WmsWarehouse warehouse = wmsWarehouseService.getById(inventory.getWarehouseId());
                    rowData.put("warehouseName", warehouse.getWarehouseName());
                    rowData.put("warehouseCode", warehouse.getWarehouseCode());
                }

                result.add(rowData);
            }

            workbook.close();

        } catch (Exception e) {
            log.error("解析Excel文件失败", e);
            throw new JeecgBootException("解析Excel文件失败: " + e.getMessage());
        }

        return result;
    }


    private String getCellStringValue(Cell cell) {
        if (cell == null) {
            return "";
        }

        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return new SimpleDateFormat("yyyy-MM-dd").format(cell.getDateCellValue());
                } else {
                    // 避免科学计数法
                    double numericValue = cell.getNumericCellValue();
                    if (numericValue == (long) numericValue) {
                        return String.valueOf((long) numericValue);
                    } else {
                        return String.valueOf(numericValue);
                    }
                }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                try {
                    return cell.getStringCellValue();
                } catch (Exception e) {
                    return String.valueOf(cell.getNumericCellValue());
                }
            default:
                return "";
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveRemainingMaterialOutbound(List<Map<String, Object>> dataList, String createBy) {
        Date currentTime = new Date();

        // 创建出库单主表
        WmsOutbound outbound = new WmsOutbound();

        // 生成出库单号
        String outboundNo = dataList.get(0).get("outboundNo").toString();
        outbound.setOutboundNo(outboundNo);

        // 设置出库单基本信息
        outbound.setOutboundType("2"); // 生产领料
        outbound.setStatus("1"); // 草稿状态
        outbound.setOutboundDate(currentTime);
        outbound.setOrderNo(dataList.get(0).get("orderNo").toString());

        // 保存出库单主表
        this.save(outbound);
        
        for (Map<String, Object> data : dataList) {
            try {
                // 创建出库单明细
                WmsOutboundDetail detail = new WmsOutboundDetail();
                detail.setOutboundId(outbound.getId());
                
                // 设置物料信息
                String materialCode = (String) data.get("materialCode");
                String materialName = (String) data.get("materialName");
                detail.setMaterialCode(materialCode != null ? materialCode.trim() : "");
                detail.setMaterialName(materialName != null ? materialName.trim() : "");
                
                // 设置数量信息
                Object quantityObj = data.get("outboundQuantity");
                BigDecimal quantity = BigDecimal.ZERO;
                if (quantityObj != null) {
                    if (quantityObj instanceof BigDecimal) {
                        quantity = (BigDecimal) quantityObj;
                    } else if (quantityObj instanceof Number) {
                        quantity = new BigDecimal(quantityObj.toString());
                    } else {
                        try {
                            quantity = new BigDecimal(quantityObj.toString());
                        } catch (NumberFormatException e) {
                            log.warn("数量格式转换失败，使用默认值0: {}", quantityObj);
                        }
                    }
                }
                detail.setQuantity(quantity);
                detail.setActualQuantity(BigDecimal.ZERO);
                
                // 设置单位
                String unit = (String) data.get("unit");
                detail.setUnit(unit != null ? unit.trim() : "");
                
                // 设置规格型号
                String specification = (String) data.get("specification");
                detail.setSpecification(specification != null ? specification.trim() : "");
                detail.setColor((String) data.get("materialColor"));
                detail.setMaterialSeries((String) data.get("series"));
                detail.setMaterialType("5");
                
                // 保存出库单明细
                wmsOutboundDetailService.save(detail);
                
            } catch (Exception e) {
                throw new RuntimeException( e.getMessage());
            }
        }
        return true;
    }
}